package org.b3log.exercise.trains;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * Graph.
 *
 * <p>
 * Using Adjacency Map to hold all nodes and arcs relationships. All noes store
 * by field member {@linkplain #nodes nodes}, and all arcs relationships with
 * nodes store by each {@linkplain Node node} itself.
 * </p>
 *
 * <p>
 *   <b>Note</b>: A node of this graph can name with a alphabet only CURRENTLY'.
 * </p>
 *
 * @author <a href="mailto:DL88250@gmail.com">Liang Ding</a>
 * @version 1.0.0.2, Mar 16, 2011
 * @see Node
 */
public final class Graph {
    /**
     * Nodes of this graph.
     */
    private Map<String, Node> nodes = new HashMap<String, Node>();

    /**
     * Constructs a graph with the specified description.
     * 
     * <p>
     * The specified description like the following format:<br/>
     * AB5, BC4, CD8, DC8, DE6, AD5, CE2, EB3, AE7
     * </p>
     * 
     * @param description the specified description
     */
    public Graph(final String description) {
        final String[] pairs =
                description.trim().split(","); // i.e. [AB5,BC4 ,...]

        for (int i = 0; i < pairs.length; i++) {
            final String pair = pairs[i].trim(); // i.e. AB5
            final String srcNodeName = pair.substring(0, 1);
            final String destNodeName = pair.substring(1, 2);

            final Node srcNode = initNode(srcNodeName);
            final Node destNode = initNode(destNodeName);

            final int distance = Integer.parseInt(pair.substring(2));
            srcNode.addDestination(destNode, distance);
        }
    }

    private Node initNode(final String nodeName) {
        Node ret = nodes.get(nodeName);
        if (null == ret) {
            ret = new Node();
            ret.setName(nodeName);
            nodes.put(nodeName, ret);
        }

        return ret;
    }

    Node getNode(final String nodeName) {
        return nodes.get(nodeName);
    }

    public int getNodesCount() {
        return nodes.size();
    }
}

/**
 * Node.
 *
 * <p>
 * Store arcs relationships by {@linkplain #nodes nodes}, means nodes connected
 * to this node directly.
 * </p>
 *
 * <p>
 *   <b>Note</b>: If two node have the same name, we consider that they are the
 *   same node.
 * </p>
 *
 * @author <a href="mailto:DL88250@gmail.com">Liang Ding</a>
 * @version 1.0.0.1, Mar 16, 2011
 * @see Graph
 */
final class Node {

    /**
     * Name of this node.
     */
    private String name;
    /**
     * Nodes of this node connect to, directly.
     */
    private Map<Node, Integer> nodes = new HashMap<Node, Integer>();

    /**
     * Gets the distance between this node and the specified node,
     *
     * @param node the specified node
     * @return distance between this node and the specified node, returns
     * {@code 0} if this node dose not connect to the specified node
     */
    public int getDistance(final Node node) {
        final Integer ret = nodes.get(node);

        if (null == ret) {
            return 0;
        }

        return ret;
    }

    public Set<Node> getDestinations() {
        return nodes.keySet();
    }

    public void addDestination(final Node node, final int distance) {
        nodes.put(node, distance);
    }

    public String getName() {
        return name;
    }

    public void setName(final String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Node[" + "name=" + name + ']';
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final Node other = (Node) obj;
        if ((this.name == null) ? (other.name != null)
                : !this.name.equals(other.name)) {
            return false;
        }
        return true;
    }

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 23 * hash + (this.name != null ? this.name.hashCode() : 0);

        return hash;
    }
}
